#include "HttpRequester.h"
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/system_properties.h>
#include <stdio.h>
#include "curl.h"
#include "RsaUtils.h"
#include <sstream>
#include <string>
#include <sys/stat.h>
#include <android/log.h>

#define HTTP_LOG_TAG            "SharkAuth"
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, HTTP_LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, HTTP_LOG_TAG, __VA_ARGS__)
#define LOG(...) __android_log_print(ANDROID_LOG_INFO, HTTP_LOG_TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, HTTP_LOG_TAG, __VA_ARGS__)

void (*pOnHttpRequestChanged)(void* data,int length );
pthread_t 		m_HttpRequestTid;
pthread_t *		m_HttpRequestThreadPtr;

volatile int keepHttpRequestRunning = 1;

typedef enum {
    JSON_NULL,
    JSON_BOOLEAN,
    JSON_NUMBER,
    JSON_STRING,
    JSON_ARRAY,
    JSON_OBJECT
} JSONType;

typedef struct JSONValue {
    JSONType type;
    union {
        int boolean;
        double number;
        char *string;
        struct JSONArray *array;
        struct JSONObject *object;
    } value;
} JSONValue;

typedef struct JSONArray {
    size_t size;
    JSONValue **items;
} JSONArray;

typedef struct JSONObject {
    char **keys;
    JSONValue **values;
    size_t size;
} JSONObject;

void skip_whitespace(const char **json) {
    while (isspace(**json)) {
        (*json)++;
    }
}

JSONValue *parse_value(const char **json);

JSONValue *parse_null(const char **json) {
    if (strncmp(*json, "null", 4) == 0) {
        *json += 4;
        JSONValue *value = (JSONValue *)malloc(sizeof(JSONValue));
        value->type = JSON_NULL;
        return value;
    }
    return NULL;
}

JSONValue *parse_boolean(const char **json) {
    if (strncmp(*json, "true", 4) == 0) {
        *json += 4;
        JSONValue *value = (JSONValue *)malloc(sizeof(JSONValue));
        value->type = JSON_BOOLEAN;
        value->value.boolean = 1;
        return value;
    } else if (strncmp(*json, "false", 5) == 0) {
        *json += 5;
        JSONValue *value = (JSONValue *)malloc(sizeof(JSONValue));
        value->type = JSON_BOOLEAN;
        value->value.boolean = 0;
        return value;
    }
    return NULL;
}

JSONValue *parse_number(const char **json) {
    char *end;
    double number = strtod(*json, &end);
    if (end != *json) {
        *json = end;
        JSONValue *value = (JSONValue *)malloc(sizeof(JSONValue));
        value->type = JSON_NUMBER;
        value->value.number = number;
        return value;
    }
    return NULL;
}

JSONValue *parse_string(const char **json) {
    if (**json == '"') {
        (*json)++;
        const char *start = *json;
        while (**json && **json != '"') {
            (*json)++;
        }
        if (**json == '"') {
            size_t length = *json - start;
            char *str = (char *)malloc(length + 1);
            strncpy(str, start, length);
            str[length] = '\0';
            (*json)++;
            JSONValue *value = (JSONValue *)malloc(sizeof(JSONValue));
            value->type = JSON_STRING;
            value->value.string = str;
            return value;
        }
    }
    return NULL;
}

JSONArray *parse_array(const char **json) {
    if (**json == '[') {
        (*json)++;
        skip_whitespace(json);
        JSONArray *array = (JSONArray *)malloc(sizeof(JSONArray));
        array->size = 0;
        array->items = NULL;

        while (**json && **json != ']') {
            JSONValue *value = parse_value(json);
            if (value) {
                array->items = (JSONValue **)realloc(array->items, (array->size + 1) * sizeof(JSONValue *));
                array->items[array->size++] = value;
            }
            skip_whitespace(json);
            if (**json == ',') {
                (*json)++;
                skip_whitespace(json);
            }
        }

        if (**json == ']') {
            (*json)++;
            return array;
        }
    }
    return NULL;
}

JSONObject *parse_object(const char **json) {
    if (**json == '{') {
        (*json)++;
        skip_whitespace(json);
        JSONObject *object = (JSONObject *)malloc(sizeof(JSONObject));
        object->size = 0;
        object->keys = NULL;
        object->values = NULL;

        while (**json && **json != '}') {
            JSONValue *key = parse_string(json);
            if (key && key->type == JSON_STRING) {
                skip_whitespace(json);
                if (**json == ':') {
                    (*json)++;
                    skip_whitespace(json);
                    JSONValue *value = parse_value(json);
                    if (value) {
                        object->keys = (char **)realloc(object->keys, (object->size + 1) * sizeof(char *));
                        object->values = (JSONValue **)realloc(object->values, (object->size + 1) * sizeof(JSONValue *));
                        object->keys[object->size] = key->value.string;
                        object->values[object->size++] = value;
                        free(key);
                    }
                }
            }
            skip_whitespace(json);
            if (**json == ',') {
                (*json)++;
                skip_whitespace(json);
            }
        }

        if (**json == '}') {
            (*json)++;
            return object;
        }
    }
    return NULL;
}

JSONValue *parse_value(const char **json) {
    skip_whitespace(json);
    if (strncmp(*json, "null", 4) == 0) {
        return parse_null(json);
    } else if (strncmp(*json, "true", 4) == 0 || strncmp(*json, "false", 5) == 0) {
        return parse_boolean(json);
    } else if (**json == '-' || isdigit(**json)) {
        return parse_number(json);
    } else if (**json == '"') {
        return parse_string(json);
    } else if (**json == '[') {
        JSONArray *array = parse_array(json);
        if (array) {
            JSONValue *value = (JSONValue *)malloc(sizeof(JSONValue));
            value->type = JSON_ARRAY;
            value->value.array = array;
            return value;
        }
    } else if (**json == '{') {
        JSONObject *object = parse_object(json);
        if (object) {
            JSONValue *value = (JSONValue *)malloc(sizeof(JSONValue));
            value->type = JSON_OBJECT;
            value->value.object = object;
            return value;
        }
    }
    return NULL;
}

void print_json_value(const JSONValue *value);

void print_json_array(const JSONArray *array) {
    printf("[");
    for (size_t i = 0; i < array->size; i++) {
        print_json_value(array->items[i]);
        if (i < array->size - 1) {
            printf(", ");
        }
    }
    printf("]");
}

void print_json_object(const JSONObject *object) {
    printf("{");
    for (size_t i = 0; i < object->size; i++) {
        printf("\"%s\": ", object->keys[i]);
        print_json_value(object->values[i]);
        if (i < object->size - 1) {
            printf(", ");
        }
    }
    printf("}");
}

void print_json_value(const JSONValue *value) {
    switch (value->type) {
    case JSON_NULL:
        printf("null");
        break;
    case JSON_BOOLEAN:
        printf(value->value.boolean ? "true" : "false");
        break;
    case JSON_NUMBER:
        printf("%f", value->value.number);
        break;
    case JSON_STRING:
        printf("\"%s\"", value->value.string);
        break;
    case JSON_ARRAY:
        print_json_array(value->value.array);
        break;
    case JSON_OBJECT:
        print_json_object(value->value.object);
        break;
    }
}

void free_json_value(JSONValue *value) {
    if (!value) return;
    switch (value->type) {
    case JSON_STRING:
        free(value->value.string);
        break;
    case JSON_ARRAY:
        for (size_t i = 0; i < value->value.array->size; i++) {
            free_json_value(value->value.array->items[i]);
        }
        free(value->value.array->items);
        free(value->value.array);
        break;
    case JSON_OBJECT:
        for (size_t i = 0; i < value->value.object->size; i++) {
            free(value->value.object->keys[i]);
            free_json_value(value->value.object->values[i]);
        }
        free(value->value.object->keys);
        free(value->value.object->values);
        free(value->value.object);
        break;
    default:
        break;
    }
    free(value);
}

JSONValue *get_value_by_key(const JSONObject *object, const char *key) {
    for (size_t i = 0; i < object->size; i++) {
        if (strcmp(object->keys[i], key) == 0) {
            return object->values[i];
        }
    }
    return NULL;
}

// Function to convert NALU data to hex string and print
void print_http_as_hex(unsigned char *nalu_data, int nalu_size) {
    char *hex_string = (char *)malloc(nalu_size * 3 + 1); // Each byte as "XX " + null terminator
    char *ptr = hex_string;

    for (int i = 0; i < nalu_size; i++) {
        ptr += sprintf(ptr, "%02X ", nalu_data[i]);
    }

    LOGD("%s\n", hex_string);
    free(hex_string);
}

static size_t write_callback(void *ptr, size_t size, size_t nmemb,
                             void *stream)
{
    int amount = (size * nmemb);
    LOGD("%s",(char *)ptr);
    (void)stream;

    const char *json = (char *)ptr;
    JSONValue *value = parse_value(&json);

    if (value && value->type == JSON_OBJECT) {
        JSONValue *data_value = get_value_by_key((JSONObject *)(value->value.object), "data");
        if (data_value && data_value->type == JSON_STRING) {
            LOGD("data: %s\n", data_value->value.string);
        }

        JSONValue *message_value = get_value_by_key((JSONObject *)(value->value.object), "message");
        if (message_value && message_value->type == JSON_STRING) {
            LOGD("message: %s\n", message_value->value.string);
        }

        JSONValue *status_value = get_value_by_key((JSONObject *)(value->value.object), "status");
        if (status_value && status_value->type == JSON_NUMBER) {
            LOGD("status: %d\n", status_value->value.number);
        }

        JSONValue *statusMessage_value = get_value_by_key((JSONObject *)(value->value.object), "statusMessage");
        if (statusMessage_value && statusMessage_value->type == JSON_STRING) {
            LOGD("statusMessage: %s\n", statusMessage_value->value.string);
        }
    } else {
        LOGD("Failed to parse JSON.\n");
    }
    free_json_value(value);
    return size * nmemb;
}

static size_t active_submit_result(void *ptr, size_t size, size_t nmemb,
                                   void *stream)
{
    int amount = (size * nmemb);
    LOGD("%s",(char *)ptr);
    (void)stream;

    const char *json = (char *)ptr;
    JSONValue *value = parse_value(&json);

    if (value && value->type == JSON_OBJECT) {
        JSONValue *status_value = get_value_by_key((JSONObject *)(value->value.object), "status");
        if (status_value && status_value->type == JSON_NUMBER) {
            LOGD("status: %d\n", status_value->value.number);
            if( 200 == status_value->value.number)
            {
                LOGD("status: active success.\n");
            }
        }

        JSONValue *data_value = get_value_by_key((JSONObject *)(value->value.object), "data");
        if (data_value && data_value->type == JSON_STRING) {
            LOGD("data: %s\n", data_value->value.string);
            // 打开文件以写入（如果文件不存在，则创建文件）
            mkdir("/mnt/vendor/persist/.xlinkhide", 0777);
            FILE *file = fopen("/mnt/vendor/persist/.xlinkhide/shark.license", "wb");

            // 检查文件是否成功打开
            if (file == NULL) {
                LOGD("Failed to open file");
                return 1;
            }

            // 要写入的字符串内容
            const char *text = data_value->value.string;

            // 写入字符串内容到文件
            if (fputs(text, file) == EOF) {
                LOGD("Failed to write to file");
                fclose(file);
                return 1;
            }

            // 关闭文件
            fclose(file);

            LOGD("Certification content written to file successfully.\n");
            keepHttpRequestRunning = 0;
        	if( pOnHttpRequestChanged != NULL )
        	{
        		char  httpData [ 2 ] = {0x01,0x01};//data[0] cmdId ,data[1] value
        		pOnHttpRequestChanged(httpData,2);
        	}
        }

        JSONValue *message_value = get_value_by_key((JSONObject *)(value->value.object), "message");
        if (message_value && message_value->type == JSON_STRING) {
            LOGD("message: %s\n", message_value->value.string);
        }

        JSONValue *statusMessage_value = get_value_by_key((JSONObject *)(value->value.object), "statusMessage");
        if (statusMessage_value && statusMessage_value->type == JSON_STRING) {
            LOGD("statusMessage: %s\n", statusMessage_value->value.string);
        }
    } else {
        LOGD("Failed to parse JSON.\n");
    }
    free_json_value(value);
    return size * nmemb;
}

void requestActiveDevice() {
    char serialno[92] = { 0 };
    char json_data [ 2048 ] = { 0 };
    __system_property_get("ro.serialno", serialno);
    LOGD("requestActiveDevice run serialno = %s . \n",serialno);

    char cert[1024] = { 0 };
    FILE *fp_cert = fopen("/system/etc/.xlinkhide/shark.cert", "r");
    if (fp_cert != NULL)
    {
        while (fgets(cert,sizeof(cert),fp_cert))
        {
        	cert[1023]='\0';		// just to be safe
//            LOGD( "shark.cert = %s \n", cert);

        }
        int fpcert_closed = fclose(fp_cert);
    }

//    const char * cert = "dW09y4j1tzIxaEbkuo1CNkQ/6bffGJ9kjOq2MRVz6h+T0MQiI9jGSgMGkTA2ykac3OUXNmd50JWJ8g1BdEYmGku38+gb9hoxK+/x4nzPSqWrxSFFBO/diiD3/Ly0aOl7bJzJQjRT9bNJ2NyqK+VkPfOErG3gsDF8j/dLFUehtOuxIOwL3PUa/NhGsDuoxIj8nd3HpKBWr7NP/18m/Pt/dkTIWqEAPGvbD7gUXp3hHpIDYDbkBl9xPv8mTrIzOzD65xO5QVjYAd/CkbfRm1sE8bb2FxMpuRzSICydN3PKUR+0etM+U9hGM+thy5sIV4xyQUK194TlDyMcbu9GL6NIXlCTjN3JPBZ7R4m/wP4BPIUr83AdxVQ8e1dCRGj0WaFyudgvCR2PQhdK+qGF5WoMYthVJGSSmrL+aRvaqATXTC4EJ+D36P0r4XGaF3wzmCAGRE6WmjOqeevByLZs/WZCs1agCL/14cVCrAELaqmrk0+FlwacJpgTxt36C/MHnAIYQANCW+1j28WD/BDwn16hB74RrlaWoEeeeDVbovaKgEqe+qRpLkaHfb+pk+r3T6g9fEvgiZUBj55hBOQtVUst5g==";
    const char * channelId = "8360622584286221";
    const char * cpuApi = "arm64-v8a";
    const char * manufacturer = "QUALCOMM";
    const char * productModel = "Bengal for arm64";
    const char * buildDescription = "bengal-user 13 TKQ1.221013.002 eng.lynx.20240517.123512 release-keys";
    const char * buildDisplayId = "SM6225.01.EAU.2024-05-17 12:30";
    const char * buildFingerprint = "qti/bengal/bengal:13/TKQ1.221013.002/eng.lynx.20240517.123512:user/release-keys";
    const char * productBoard = "bengal";
    const char * androidSdkVersion = "33";

    //    \"cpuAbi\":\"%s\",
    sprintf( json_data, "{\"uuid\":\"%s\",\"certificate\":\"%s\",\"channelId\":\"%s\","
             "\"cpuAbi\":\"%s\",\"manufacturer\":\"%s\",\"productModel\":\"%s\","
             "\"buildDescription\":\"%s\",""\"buildDisplayId\":\"%s\", \"buildFingerprint\":\"%s\", \"cpuAbi\":\"%s\", "
             "\"androidSdkVersion\":\"%s\"     }",
             serialno,cert,channelId,
             cpuApi,manufacturer,productModel,
             buildDescription,buildDisplayId,buildFingerprint,
             productBoard,androidSdkVersion);

    LOGD("requestActiveDevice json_data = %s . \n",json_data);

    CURL *curl = NULL;
    CURLcode res;

    struct curl_slist *headers = NULL;
    headers = curl_slist_append(headers, "Content-Type: application/json");

    curl = curl_easy_init();
    if( curl ) {
        curl_easy_setopt(curl, CURLOPT_URL, "http://120.24.79.21:18080/activate/submit");
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_data);

        /* Write callback */
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, active_submit_result);

        res = curl_easy_perform(curl);

        if(res != CURLE_OK)
        {
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
            LOGD("curl_easy_perform() failed: %s \n",curl_easy_strerror(res));
        }
        curl_easy_cleanup(curl);
        curl_slist_free_all(headers);
    }

}

void *httpRequesterThread(void *arg) {
    keepHttpRequestRunning = 1;
    LOGD("httpRequesterThread run. \n");

    uint_t deviceActivated = 0;
    char serialno[92] = { 0 };
    __system_property_get("ro.serialno", serialno);
    LOGD("httpRequesterThread run serialno = %s . \n",serialno);

    if(access("/mnt/vendor/persist/.xlinkhide/shark.license",F_OK) == 0) {
        LOGD("httpRequesterThread run shark.license exist. \n");
        char license[1024] = { 0 };
        FILE *fp_license = fopen("/mnt/vendor/persist/.xlinkhide/shark.license", "r");
        if (fp_license != NULL)
        {
            while (fgets(license,sizeof(license),fp_license))
            {
                license[1023]='\0';		// just to be safe
                LOGD( "shark.license = %s \n", license);

            }
            int fplicense_closed = fclose(fp_license);
        }
        unsigned char * decryptedtext = RsaUtils_Init( (unsigned char *)license);
        if( decryptedtext != NULL )
        {
            LOGD("httpRequesterThread run shark.license exist decryptedText = %s \n",decryptedtext);
            std::string content = std::string((char *)decryptedtext);
            std::stringstream stream(content);
            std::string param = "";
            std::string activeCode = "";
            std::string deviceId = "";
            std::string productCode = "";
            std::string expiredTime = "";
            uint8_t index = 0;
            while (std::getline(stream, param, ':'))
            {
                LOGD("shark.license param = %s . \n",param.c_str());
                switch (index) {
                case 0:
                    activeCode = std::string(param);
                    LOGD("shark.license activeCode = %s . \n",activeCode.c_str());
                    break;
                case 1:
                    deviceId = std::string(param);
                    LOGD("shark.license deviceId = %s . \n",deviceId.c_str());
                    if(std::string(serialno) == deviceId )
                    {
                    	deviceActivated = 1;
                    	LOGD("shark.license deviceId = %s are same with the box serialno. \n",deviceId.c_str());
                    	if( pOnHttpRequestChanged != NULL )
                    	{
                    		char  httpData [ 2 ] = {0x01,0x01};//data[0] cmdId ,data[1] value
                    		pOnHttpRequestChanged(httpData,2);
                    	}
                    }else{
                    	deviceActivated = 0;
                    	LOGD("shark.license deviceId = %s are different with the box serialno. \n",deviceId.c_str());
                    	if( pOnHttpRequestChanged != NULL )
                    	{
                    		char  httpData [ 2 ] = {0x01,0x00};//data[0] cmdId ,data[1] value
                    		pOnHttpRequestChanged(httpData,2);
                    	}
                    }

                    break;
                case 2:
                    productCode = std::string(param);
                    LOGD("shark.license productCode = %s . \n",productCode.c_str());
                    break;
                default:
                    break;
                }
                ++index;
            }
        }

    }

    keepHttpRequestRunning &= ~deviceActivated;
    while( keepHttpRequestRunning )
    {
        requestActiveDevice();
//		CURL *curl = NULL;
//		CURLcode res;
//
//		struct curl_slist *headers = NULL;
//		headers = curl_slist_append(headers, "Content-Type: application/json");
//
//		curl = curl_easy_init();
//		if( curl ) {
//		    curl_easy_setopt(curl, CURLOPT_URL, "http://120.24.79.21:18080/activate/recheck");
//		    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
//		    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_data);
//
//		    /* Write callback */
//		    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
//
//		    res = curl_easy_perform(curl);
//
//		    if(res != CURLE_OK)
//		    {
//		    	fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
//		    	LOGD("curl_easy_perform() failed: %s \n",curl_easy_strerror(res));
//		    }
//		    curl_easy_cleanup(curl);
//		    curl_slist_free_all(headers);
//		}

        sleep(3);

    }
    return (NULL);
}

void start_http_request(void (*pFunc)(void* data,int length)) {
    pOnHttpRequestChanged = pFunc;
    int ret;
    if ((ret = pthread_create(&m_HttpRequestTid, NULL, httpRequesterThread, NULL))
            != 0) {
        LOGD("start_http_request thread create failed!\n");
    } else {
        m_HttpRequestThreadPtr = &m_HttpRequestTid;
        LOGD("start_http_request thread create successed!\n");
    }
}

void stop_http_request( ) {
    keepHttpRequestRunning = 0;
    if( m_HttpRequestThreadPtr != NULL )
    {
        pthread_join(m_HttpRequestTid, NULL);
        m_HttpRequestThreadPtr = NULL;
    }
}
